---
title: "【翻译】讨论Nonblocking IO是否会返回-1和EINTR"
categories:
- linux
tags:
---

<div id="content">
<div id="table-of-contents">
<h2>Table of Contents</h2>
<div id="text-table-of-contents">
<ul>
<li><a href="#org3b187b8">Intro</a></li>
<li><a href="#org02fda78">防失效原文</a>
<ul>
<li><a href="#org62334f7">Title</a></li>
<li><a href="#org1f0c69c">Question</a></li>
<li><a href="#orgb2c0c74">Answer</a></li>
</ul>
</li>
</ul>
</div>
</div>
<div class="outline-2" id="outline-container-org3b187b8">
<h2 id="org3b187b8">Intro</h2>
<div class="outline-text-2" id="text-org3b187b8">
<p>
#+BEING_QUOTE
<a href="https://stackoverflow.com/questions/14134440/eintr-and-non-blocking-calls">https://stackoverflow.com/questions/14134440/eintr-and-non-blocking-calls</a>
#+END_QUOTE
</p>
<p>
read, write这些函数，在阻塞的情况下如果被中断会返回-1，并且将errno设置为EINTR，这时我们需要处理这种情况。
那么，在非阻塞的情况下，例如，将socket设置为O_NONBLOCK，我们是否还需要处理以上的EINTR呢？
</p>
<p>
对于这个问题，没法给出一个绝对的答案，而且各操作系统之间也有差异，但是我认为non-blocing IO socket永远不会出现EINTR。
如果你查看各各操作系统上的socket函数：bind(), connect(), send(), receive()，或者查阅POXIS标准，你会发现一些有意思的东西：
这些函数都会返回-1并设置errno=EINTR，除了bind()，bind()不会有EINTR的失败。而且bind也是这几个函数中唯一一个默认不阻塞的。
就好像只有阻塞调用的函数会出现EINTR的失败，面如果设置了O_NONBLOCK，这些函数永远不会阻塞。
</p>
<p>
在逻辑层面来讲，非阻塞调用以EINTR失败是没有意义的。例如，你使用阻塞式的IO，然后调用read()，这个函数调用处于等待中，这时一个信号被发送到进程，导致read退出阻塞。
那么系统如何处理这种情况？如果系统说read成功了，那系统就是在撒谎，因为并没有数据读到了，这个函数没有成功。
如果系统说read成功了，但是读取了0字节？这同样不是正确的，因为read返回0一般表示EOF，
调用者所期望的结果是，read没有成功或读到了数据，但也没有因为错误失败。因此，read需要返回失败，但是需要设置一个errno。
所有其他的errno表示fd的严重错误，系统不能将errno设置为那些值，
所以，系统将errno设置为EINTR，它表示：STREAM并没有出现错误，read的调用是由于中断面失败的。如果没有被中断，read调用还是能成功的，所以请重试。
</p>
<p>
如果使用的是non-blocing IO，上面的情况就不会出现了。
read函数永远不会阻塞，如果不能立即读取数据，read()返回失败，然后设置errno为EAGAIN(POSIX)，或者EWOULDBLOCK(非官方，在linux上这两个errno的值是相同的，只是一个别名）。
EAGAIN表示：当前没有数据能读，因此你的read会等待数据到达，但是你设置了非阻塞，所以read失败了。
</p>
<p>
当然，在non-blocing IO中，read函数还是会因为信号面中断，但是系统为什么要让调用者知道呢？
所有的函数调用，无论是系统函数，还是用户函数，都会暂时被信号中断，是 <b>每一个</b> ，没有例外。
如果每个信号，系统都通知，所有的系统函数都会以EINTR失败。
然而，即使有中断信号，函数一般还是完成它所有的任务，这就是中断无关紧要的原因。
EINTR是用于告诉调用者请求的操作因为信号中断面没有执行。但是在non-blocing IO中，函数没有理由不执行read或write，除非是它当前暂时无法执行，能通过正确的errno来表明这种情况的发生。
</p>
<p>
为了证明我的观点，我查阅了MacOS (10.8)的内核，这个版本仍然是在量基于FreeBSD内核的。貌似我的观点是正确的。
如果一个当前无法read，没有数据，内核检查fd的O_NONBLOCK标志，如果设置了O_NONBLOCK就马上返回EAGAIN；如果没有，系统通过调用msleep()，将当前线程投入睡眠状态。
方法的文档在<a href="http://www.gsp.com/cgi-bin/man.cgi?section=9&amp;topic=msleep">这里</a>。 这个方法将线程投入睡眠，直到有数据能够读。或超时(timeout，例如socket的timeout)。当有信号到达时，线程也会被唤醒，这种情况下，msleep()返回EINTR。
因此是实际上是msleep返回的EINTR，当O_NONBLOCK被设置的时候，msleep不会被调用，因此也不会有EINTR了。
</p>
<p>
当然，这是MacOS/FressBSD的实现，其他系统也许不一样。但是在部分系统会试着保持API的一致性。如果一个系统打破了这种假设，那它应该不是故意的，甚至你去report bug，他们还会修复这个问题。
</p>
</div>
</div>
<div class="outline-2" id="outline-container-org02fda78">
<h2 id="org02fda78">防失效原文</h2>
<div class="outline-text-2" id="text-org02fda78">
</div>
<div class="outline-3" id="outline-container-org62334f7">
<h3 id="org62334f7">Title</h3>
<div class="outline-text-3" id="text-org62334f7">
<p>
EINTR and non-blocking calls
</p>
</div>
</div>
<div class="outline-3" id="outline-container-org1f0c69c">
<h3 id="org1f0c69c">Question</h3>
<div class="outline-text-3" id="text-org1f0c69c">
<p>
As is known, some blocking calls like read and write would return -1 and set errno to EINTR, and we need handle this.
My question is: Does this apply for non-blocking calls, e.g, set socket to O_NONBLOCK?
Since some articles and sources I have read said non-blocking calls don't need bother with this, but I have found no authoritative reference about it. If so, does it apply cross different implementations?
</p>
</div>
</div>
<div class="outline-3" id="outline-container-orgb2c0c74">
<h3 id="orgb2c0c74">Answer</h3>
<div class="outline-text-3" id="text-orgb2c0c74">
<p>
I cannot give you a definitive answer to this question, and the answer may further vary from system to system, but I would expect a non-blocking socket to never fail with EINTR. If you take a look at the man pages of various systems for the following socket functions bind(), connect(), send(), and receive(), or look those up in the POSIX standard, you'll notice something interesting: All these functions except one may return -1 and set errno to EINTR. The one function that is not documented to ever fail with EINTR is bind(). And bind() is also the only function of that list that will never block by default. So it seems that only blocking functions may fail because of EINTR, including read() and write(), yet if these functions never block, they also will never fail with EINTR and if you use O_NONBLOCK, those functions will never block.
</p>
<p>
It would also make no sense from a logical perspective. E.g. consider you are using blocking I/O and you call read() and this call has to block, but while it was blocking, a signal is sent to your process and thus the read request is unblocked. How should the system handle this situation? Claiming that read() did succeed? That would be a lie, it did not succeed because no data was read. Claiming it did succeed, but zero bytes data were read? This wouldn't be correct either, since a "zero read result" is used to indicate end-of-stream (or end-of-file), so your process would to assume that no data was read, because the end of a file has been reached (or a socket/pipe has been closed at other end), which simply isn't the case. The end-of-file (or end-of-stream) has not been reached, if you call read() again, it will be able to return more data. So that would also be a lie. You expectation is that this read call either succeeds and reads data or fails with an error. Thus the read call has to fail and return -1 in that case, but what errno value shall the system set? All the other error values indicate a critical error with the file descriptor, yet there was no critical error and indicating such an error would also be a lie. That's why errno is set to EINTR, which means: "There was nothing wrong with the stream. Your read call just failed, because it was interrupted by a signal. If it wasn't interrupted, it may still have succeeded, so if you still care for the data, please try again."
</p>
<p>
If you now switch to non-blocking I/O, the situation of above never arises. The read call will never block and if it cannot read data immediately, it will fail with an error EAGAIN (POSIX) or EWOULDBLOCK (unofficial, on Linux both are the same error, just alternative names for it), which means: "There is no data available right now and thus your read call would have to block and wait for data arriving, but blocking is not allowed, so it failed instead." So there is an error for every situation that may arise. 
</p>
<p>
Of course, even with non-blocking I/O, the read call may have temporarily interrupted by a signal but why would the system have to indicate that? Every function call, whether this is a system function or one written by the user, may be temporarily interrupted by a signal, really every single one, no exception. If the system would have to inform the user whenever that happens, all system functions could possibly fail because of EINTR. However, even if there was a signal interruption, the functions usually perform their task all the way to the end, that's why this interruption is irrelevant. The error EINTR is used to tell the caller that the action he has requested was not performed because of a signal interruption, but in case of non-blocking I/O, there is no reason why the function should not perform the read or the write request, unless it cannot be performed right now, but then this can be indicated by an appropriate error.
</p>
<p>
To confirm my theory, I took a look at the kernel of MacOS (10.8), which is still largely based on the FreeBSD kernel and it seems to confirm the suspicion. If a read call is currently not possible, as no data are available, the kernel checks for the O_NONBLOCK flag in the file descriptor flags. If this flag is set, it fails immediately with EAGAIN. If it is not set, it puts the current thread to sleep by calling a function named msleep(). The function is documented here (as I said, OS X uses plenty of FreeBSD code in its kernel). This function causes the current thread to sleep until it is explicitly woken up (which is the case if data becomes ready for reading) or a timeout has been hit (e.g. you can set a receive timeout on sockets). Yet the thread is also woken up, if a signal is delivered, in which case msleep() itself returns EINTR and the next higher layer just passes this error through. So it is msleep() that produces the EINTR error, but if the O_NONBLOCK flag is set, msleep() is never called in the first place, hence this error cannot be returned.
</p>
<p>
Of course that was MacOS/FreeBSD, other systems may be different, but since most systems try to keep at least a certain level of consistency among these APIs, if a system breaks the assumption, that non-blocking I/O calls can never fail because of EINTR, this is probably not by intention and may even get fixed if your report it.
</p>
</div>
</div>
</div>
</div>
<div class="status" id="postamble">
<p class="author">Author: gdme1320</p>
<p class="validation"><a href="http://validator.w3.org/check?uri=referer">Validate</a></p>
</div>
